package com.kis.core.scan;

import com.kis.core.annotation.RpcClient;
import com.kis.core.annotation.RpcServer;
import com.kis.core.scan.factory.LoadBalanceHessianProxyFactoryBean;
import com.kis.core.util.StringTools;
import com.kis.core.util.enums.ConstEnum;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.remoting.caucho.HessianServiceExporter;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarFile;

import static org.springframework.util.Assert.notNull;

/**
 * Created by wongloong on 16-5-5.
 */
public class Scan {
    private String scanPackage;
    private ApplicationContext applicationContext;
    private Enum type;
    public Map<String, BeanDefinitionBuilder> beans = new ConcurrentHashMap<String, BeanDefinitionBuilder>();

    public Map<String, BeanDefinitionBuilder> doScan() {
        notNull(scanPackage, "scan package is must be not null");
        String allPath = Scan.class.getResource("/").toString();
        String realPath = getRealPath(allPath);
        String scanPath = realPath + scanPackage.replaceAll("\\.", "/");
        ScanPackage(scanPath);
        if (beans.isEmpty()) {
            scanPackgeInJarFile(scanPath);
        }
        return beans;
    }

    private void scanPackgeInJarFile(String path) {
        try {
            if (path.indexOf(".jar!") > -1) {//判断jar文件
                String[] paths = path.split("!");
                if (paths.length == 2) {
                    String jarUrl = paths[0];
                    String packageName = paths[1];
                    JarFile jarFile = new JarFile(jarUrl);
                    Enumeration enumeration = jarFile.entries();
                    List<String> classPackage = StringTools.getClassPaths(enumeration, scanPackage);
                    for (String className : classPackage) {
                        assemBean4Server(className);
                    }
                } else {
                    throw new RuntimeException("路径" + path + "包含多个'!'");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void ScanPackage(String path) {
        File file = new File(path);
        File[] files = file.listFiles();
        if (null != files) {
            for (File f : files) {
                if (f.isFile()) {
                    try {
                        String classPath = f.getAbsolutePath().replace("/", ".");
                        if (classPath.length() > 6 && classPath.endsWith(".class")) {
                            if (classPath.indexOf(("classes." + scanPackage)) >= 0) {
                                classPath = classPath.substring(8 + classPath.indexOf("classes." + scanPackage));
                            } else {
                                continue;
                            }
                            Class<?> aClass = Class.forName(classPath.substring(0, classPath.length() - 6));
                            if (type.equals(ConstEnum.SERVER)) {
                                if (aClass.isAnnotationPresent(RpcServer.class)) {
                                    RpcServer rpc = aClass.getAnnotation(RpcServer.class);
                                    String url = rpc.url();
                                    String preUrl = rpc.prefixUrl();
                                    String interfacePath = getInterfaceName(aClass);
                                    Class interfaceClass = Class.forName(interfacePath);
                                    String serviceBeanName = getServiceBeanName(aClass);
                                    String visitUrl = preUrl + url;
                                    BeanDefinitionBuilder hessianBean = BeanDefinitionBuilder.rootBeanDefinition(HessianServiceExporter.class);
                                    hessianBean.addPropertyValue("Service", applicationContext.getBean(serviceBeanName));
                                    hessianBean.addPropertyValue("ServiceInterface", interfaceClass);
                                    beans.put(visitUrl, hessianBean);
                                }
                                System.out.println("scan server");
                            } else if (type.equals(ConstEnum.CLIENT)) {//do scan client @RpcClient but it's used
                                Field[] fields = aClass.getDeclaredFields();
                                for (Field field : fields) {
                                    if (field.isAnnotationPresent(RpcClient.class)) {
                                        RpcClient rpcClient = field.getAnnotation(RpcClient.class);
                                        String serverName = rpcClient.serverName();
                                        String accessPath = rpcClient.accessPath();
                                        String interfaceClassName = field.getType().getName();
                                        notNull(serverName, "the annotation field serverName must be not null at " + aClass.getName() + " the field is " + field.getName());
                                        notNull(accessPath, "the annotation field accesspath must be not null at " + aClass.getName() + "the filed is " + field.getName());
                                        notNull(interfaceClassName, "Can not find the Interface in the field" + field.getType());
                                        BeanDefinitionBuilder serviceBean = BeanDefinitionBuilder.rootBeanDefinition(LoadBalanceHessianProxyFactoryBean.class);
                                        serviceBean.addPropertyValue("ServerName", serverName);
                                        serviceBean.addPropertyValue("AccessPath", accessPath);
                                        serviceBean.addPropertyValue("ServiceInterface", Class.forName(interfaceClassName));
                                        beans.put(field.getName(), serviceBean);
                                    }
                                }
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                } else {
                    ScanPackage(f.getPath());
                }
            }
        }
    }

    public void assemBean4Server(String className) {
        Class<?> aClass = null;
        try {
            aClass = Class.forName(className.substring(0, className.length() - 6));
            if (aClass.isAnnotationPresent(RpcServer.class)) {
                RpcServer rpc = aClass.getAnnotation(RpcServer.class);
                String url = rpc.url();
                String preurl = rpc.prefixUrl();
                String interfacePath = getInterfaceName(aClass);
                Class interfaceClass = Class.forName(interfacePath);
                String serviceBeanName = getServiceBeanName(aClass);
                String visitUrl = preurl + url;
                BeanDefinitionBuilder hessianBean = BeanDefinitionBuilder.rootBeanDefinition(HessianServiceExporter.class);
                hessianBean.addPropertyValue("Service", applicationContext.getBean(serviceBeanName));
                hessianBean.addPropertyValue("ServiceInterface", interfaceClass);
                beans.put(visitUrl, hessianBean);
            }
            System.out.println("scan server");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private String getInterfaceName(Class aClass) {
        String interfacePath = "";
        Type[] types = aClass.getGenericInterfaces();
        if (null != types && types.length > 0) {
            for (Type type : types) {
                if (type.getTypeName().indexOf(scanPackage) > -1) {
                    interfacePath = type.getTypeName();
                    break;
                }
            }
            if (StringUtils.isEmpty(interfacePath)) {
                throw new RuntimeException("service impl some interface but the interface is not in " + scanPackage);
            }
        } else {
            throw new RuntimeException("service must be impl some interface");
        }
        return interfacePath;
    }

    private String getServiceBeanName(Class aClass) {
        String serviceBeanName = "";
        if (aClass.isAnnotationPresent(Service.class)) {
            Service service = (Service) aClass.getAnnotation(Service.class);
            serviceBeanName = service.value();
        }
        if (StringUtils.isEmpty(serviceBeanName)) {
            throw new RuntimeException("can not find the annotation of @Service in " + aClass.getName() + ",please check it");
        }
        return serviceBeanName;
    }

    private String getRealPath(String allPath) {
        String realPath = "";
        if (!StringUtils.isEmpty(allPath)) {
            if (allPath.indexOf(":") > -1) {
                int length = allPath.split(":").length;
                realPath = allPath.split(":")[length - 1];
            } else {
                throw new RuntimeException("file path has a problem,please check it " + allPath);
            }
        }
        return realPath;
    }

    public Scan(String scanPackage, ApplicationContext applicationContext, Enum type) {
        this.scanPackage = scanPackage;
        this.applicationContext = applicationContext;
        this.type = type;
    }

    public String getScanPackage() {
        return scanPackage;
    }

    public void setScanPackage(String scanPackage) {
        this.scanPackage = scanPackage;
    }
}
